﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ContainerManager.cs" company="">
//   
// </copyright>
// <summary>
//   The container manager.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace Skymate.Engines.DependencyManagement
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    

    using Castle.MicroKernel.Registration;
    using Castle.Windsor;
    using Castle.Windsor.Installer;

    /// <summary>
    ///     The container manager.
    /// </summary>
    public class IocManager : IIocManager
    {

        /// <summary>
        /// The Singleton instance.
        /// </summary>
        public static IocManager Instance { get; private set; }

        /// <summary>
        /// Reference to the Castle Windsor Container.
        /// </summary>
        public IWindsorContainer IocContainer { get; private set; }

        /// <summary>
        /// List of all registered conventional registrars.
        /// </summary>
        private readonly List<IConventionalDependencyRegistrar> _conventionalRegistrars;

        static IocManager()
        {
            Instance = new IocManager();
        }

        /// <summary>
        /// Creates a new <see cref="IocManager"/> object.
        /// Normally, you don't directly instantiate an <see cref="IocManager"/>.
        /// This may be useful for test purposes.
        /// </summary>
        public IocManager()
        {
            IocContainer = new WindsorContainer();
            _conventionalRegistrars = new List<IConventionalDependencyRegistrar>();

            //Register self!
            IocContainer.Register(
                Component.For<IocManager, IIocManager, IIocRegistrar, IIocResolver>().UsingFactoryMethod(() => this)
                );
        }

        /// <summary>
        /// Adds a dependency registrar for conventional registration.
        /// </summary>
        /// <param name="registrar">dependency registrar</param>
        public void AddConventionalRegistrar(IConventionalDependencyRegistrar registrar)
        {
            _conventionalRegistrars.Add(registrar);
        }

        /// <summary>
        /// Registers types of given assembly by all conventional registrars. See <see cref="AddConventionalRegistrar"/> method.
        /// </summary>
        /// <param name="assembly">Assembly to register</param>
        public void RegisterAssemblyByConvention(Assembly assembly)
        {
            RegisterAssemblyByConvention(assembly, new ConventionalRegistrationConfig());
        }

        /// <summary>
        /// Registers types of given assembly by all conventional registrars. See <see cref="AddConventionalRegistrar"/> method.
        /// </summary>
        /// <param name="assembly">Assembly to register</param>
        /// <param name="config">Additional configuration</param>
        public void RegisterAssemblyByConvention(Assembly assembly, ConventionalRegistrationConfig config)
        {
            var context = new ConventionalRegistrationContext(assembly, this, config);

            foreach (var registerer in _conventionalRegistrars)
            {
                registerer.RegisterAssembly(context);
            }

            if (config.InstallInstallers)
            {
                IocContainer.Install(FromAssembly.Instance(assembly));
            }
        }

        /// <summary>
        /// Registers a type as self registration.
        /// </summary>
        /// <typeparam name="TType">Type of the class</typeparam>
        /// <param name="lifeStyle">Lifestyle of the objects of this type</param>
        public void Register<TType>(DependencyLifeStyle lifeStyle = DependencyLifeStyle.Singleton) where TType : class
        {
            IocContainer.Register(ApplyLifestyle(Component.For<TType>(), lifeStyle));
        }

        /// <summary>
        /// Registers a type as self registration.
        /// </summary>
        /// <param name="type">Type of the class</param>
        /// <param name="lifeStyle">Lifestyle of the objects of this type</param>
        public void Register(Type type, DependencyLifeStyle lifeStyle = DependencyLifeStyle.Singleton)
        {
            IocContainer.Register(ApplyLifestyle(Component.For(type), lifeStyle));
        }

        /// <summary>
        /// Registers a type with it's implementation.
        /// </summary>
        /// <typeparam name="TType">Registering type</typeparam>
        /// <typeparam name="TImpl">The type that implements <see cref="TType"/></typeparam>
        /// <param name="lifeStyle">Lifestyle of the objects of this type</param>
        public void Register<TType, TImpl>(DependencyLifeStyle lifeStyle = DependencyLifeStyle.Singleton)
            where TType : class
            where TImpl : class, TType
        {
            IocContainer.Register(ApplyLifestyle(Component.For<TType, TImpl>().ImplementedBy<TImpl>(), lifeStyle));
        }

        /// <summary>
        /// Registers a class as self registration.
        /// </summary>
        /// <param name="type">Type of the class</param>
        /// <param name="impl">The type that implements <paramref name="type"/></param>
        /// <param name="lifeStyle">Lifestyle of the objects of this type</param>
        public void Register(Type type, Type impl, DependencyLifeStyle lifeStyle = DependencyLifeStyle.Singleton)
        {
            IocContainer.Register(ApplyLifestyle(Component.For(type, impl).ImplementedBy(impl), lifeStyle));
        }

        /// <summary>
        /// Checks whether given type is registered before.
        /// </summary>
        /// <param name="type">Type to check</param>
        public bool IsRegistered(Type type)
        {
            return IocContainer.Kernel.HasComponent(type);
        }

        /// <summary>
        /// Checks whether given type is registered before.
        /// </summary>
        /// <typeparam name="TType">Type to check</typeparam>
        public bool IsRegistered<TType>()
        {
            return IocContainer.Kernel.HasComponent(typeof(TType));
        }

        /// <summary>
        /// Gets an object from IOC container.
        /// Returning object must be Released (see <see cref="IIocResolver.Release"/>) after usage.
        /// </summary> 
        /// <typeparam name="T">Type of the object to get</typeparam>
        /// <returns>The instance object</returns>
        public T Resolve<T>()
        {
            return IocContainer.Resolve<T>();
        }

        /// <summary>
        /// Gets an object from IOC container.
        /// Returning object must be Released (see <see cref="IIocResolver.Release"/>) after usage.
        /// </summary> 
        /// <typeparam name="T">Type of the object to get</typeparam>
        /// <param name="argumentsAsAnonymousType">Constructor arguments</param>
        /// <returns>The instance object</returns>
        public T Resolve<T>(object argumentsAsAnonymousType)
        {
            return IocContainer.Resolve<T>(argumentsAsAnonymousType);
        }

        /// <summary>
        /// Gets an object from IOC container.
        /// Returning object must be Released (see <see cref="IIocResolver.Release"/>) after usage.
        /// </summary> 
        /// <param name="type">Type of the object to get</param>
        /// <returns>The instance object</returns>
        public object Resolve(Type type)
        {
            return IocContainer.Resolve(type);
        }

        /// <summary>
        /// Gets an object from IOC container.
        /// Returning object must be Released (see <see cref="IIocResolver.Release"/>) after usage.
        /// </summary> 
        /// <param name="type">Type of the object to get</param>
        /// <param name="argumentsAsAnonymousType">Constructor arguments</param>
        /// <returns>The instance object</returns>
        public object Resolve(Type type, object argumentsAsAnonymousType)
        {
            return IocContainer.Resolve(type, argumentsAsAnonymousType);
        }

        /// <summary>
        /// Releases a pre-resolved object. See Resolve methods.
        /// </summary>
        /// <param name="obj">Object to be released</param>
        public void Release(object obj)
        {
            IocContainer.Release(obj);
        }

        /// <inheritdoc/>
        public void Dispose()
        {
            IocContainer.Dispose();
        }

        private static ComponentRegistration<T> ApplyLifestyle<T>(ComponentRegistration<T> registration, DependencyLifeStyle lifeStyle)
            where T : class
        {
            switch (lifeStyle)
            {
                case DependencyLifeStyle.Transient:
                    return registration.LifestyleTransient();
                case DependencyLifeStyle.Singleton:
                    return registration.LifestyleSingleton();
                default:
                    return registration;
            }
        }
    

    /*
            /// <summary>
            /// Initializes a new instance of the <see cref="ContainerManager"/> class.
            /// </summary>
            /// <param name="container">
            /// The container.
            /// </param>
            public ContainerManager(IWindsorContainer container)
            {
                this.Container = container;
            }

            /// <summary>
            ///     Gets the container.
            /// </summary>
            /*public IContainer Container { get; private set; }
    #1#
            public IWindsorContainer Container { get; set; }

            /// <summary>
            /// The resolve.
            /// </summary>
            /// <param name="key">
            /// The key.
            /// </param>
            /// <param name="scope">
            /// The scope.
            /// </param>
            /// <typeparam name="T">
            /// </typeparam>
            /// <returns>
            /// The <see cref="T"/>.
            /// </returns>
            public T Resolve<T>(string key = "", ILifetimeScope scope = null) where T : class
            {
                if (string.IsNullOrEmpty(key))
                {
                    return (scope ?? this.Scope()).Resolve<T>();
                }

                return (scope ?? this.Scope()).ResolveKeyed<T>(key);
            }

            /// <summary>
            /// The resolve named.
            /// </summary>
            /// <param name="name">
            /// The name.
            /// </param>
            /// <param name="scope">
            /// The scope.
            /// </param>
            /// <typeparam name="T">
            /// </typeparam>
            /// <returns>
            /// The <see cref="T"/>.
            /// </returns>
            public T ResolveNamed<T>(string name, ILifetimeScope scope = null) where T : class
            {
                return (scope ?? this.Scope()).ResolveNamed<T>(name);
            }

            /// <summary>
            /// The resolve.
            /// </summary>
            /// <param name="type">
            /// The type.
            /// </param>
            /// <param name="scope">
            /// The scope.
            /// </param>
            /// <returns>
            /// The <see cref="object"/>.
            /// </returns>
            public object Resolve(Type type, ILifetimeScope scope = null)
            {
                return (scope ?? this.Scope()).Resolve(type);
            }

            /// <summary>
            /// The resolve named.
            /// </summary>
            /// <param name="name">
            /// The name.
            /// </param>
            /// <param name="type">
            /// The type.
            /// </param>
            /// <param name="scope">
            /// The scope.
            /// </param>
            /// <returns>
            /// The <see cref="object"/>.
            /// </returns>
            public object ResolveNamed(string name, Type type, ILifetimeScope scope = null)
            {
                return (scope ?? this.Scope()).ResolveNamed(name, type);
            }

            /// <summary>
            /// The resolve all.
            /// </summary>
            /// <param name="key">
            /// The key.
            /// </param>
            /// <param name="scope">
            /// The scope.
            /// </param>
            /// <typeparam name="T">
            /// </typeparam>
            /// <returns>
            /// The <see cref="T[]"/>.
            /// </returns>
            public T[] ResolveAll<T>(string key = "", ILifetimeScope scope = null)
            {
                if (string.IsNullOrEmpty(key))
                {
                    return (scope ?? this.Scope()).Resolve<IEnumerable<T>>().ToArray();
                }

                return (scope ?? this.Scope()).ResolveKeyed<IEnumerable<T>>(key).ToArray();
            }

            /// <summary>
            /// The resolve unregistered.
            /// </summary>
            /// <param name="scope">
            /// The scope.
            /// </param>
            /// <typeparam name="T">
            /// </typeparam>
            /// <returns>
            /// The <see cref="T"/>.
            /// </returns>
            public T ResolveUnregistered<T>(ILifetimeScope scope = null) where T : class
            {
                return this.ResolveUnregistered(typeof(T), scope) as T;
            }

            /// <summary>
            /// The resolve unregistered.
            /// </summary>
            /// <param name="type">
            /// The type.
            /// </param>
            /// <param name="scope">
            /// The scope.
            /// </param>
            /// <returns>
            /// The <see cref="object"/>.
            /// </returns>
            /// <exception cref="SkymateException">
            /// </exception>
            public object ResolveUnregistered(Type type, ILifetimeScope scope = null)
            {
                var constructors = type.GetConstructors();
                foreach (var constructor in constructors)
                {
                    try
                    {
                        var parameters = constructor.GetParameters();
                        var parameterInstances = new List<object>();
                        foreach (var parameter in parameters)
                        {
                            var service = this.Resolve(parameter.ParameterType, scope);
                            if (service == null)
                            {
                                throw new SkymateException("Unkown dependency");
                            }

                            parameterInstances.Add(service);
                        }

                        return Activator.CreateInstance(type, parameterInstances.ToArray());
                    }
                    catch (SkymateException)
                    {
                    }
                }

                throw new SkymateException("No contructor was found that had all the dependencies satisfied.");
            }

            /// <summary>
            /// The try resolve.
            /// </summary>
            /// <param name="serviceType">
            /// The service type.
            /// </param>
            /// <param name="scope">
            /// The scope.
            /// </param>
            /// <param name="instance">
            /// The instance.
            /// </param>
            /// <returns>
            /// The <see cref="bool"/>.
            /// </returns>
            public bool TryResolve(Type serviceType, ILifetimeScope scope, out object instance)
            {
                return (scope ?? this.Scope()).TryResolve(serviceType, out instance);
            }

            /// <summary>
            /// The try resolve.
            /// </summary>
            /// <param name="scope">
            /// The scope.
            /// </param>
            /// <param name="instance">
            /// The instance.
            /// </param>
            /// <typeparam name="T">
            /// </typeparam>
            /// <returns>
            /// The <see cref="bool"/>.
            /// </returns>
            public bool TryResolve<T>(ILifetimeScope scope, out T instance)
            {
                return (scope ?? this.Scope()).TryResolve(out instance);
            }

            /// <summary>
            /// The is registered.
            /// </summary>
            /// <param name="serviceType">
            /// The service type.
            /// </param>
            /// <param name="scope">
            /// The scope.
            /// </param>
            /// <returns>
            /// The <see cref="bool"/>.
            /// </returns>
            public bool IsRegistered(Type serviceType, ILifetimeScope scope = null)
            {
                return (scope ?? this.Scope()).IsRegistered(serviceType);
            }

            /// <summary>
            /// The resolve optional.
            /// </summary>
            /// <param name="serviceType">
            /// The service type.
            /// </param>
            /// <param name="scope">
            /// The scope.
            /// </param>
            /// <returns>
            /// The <see cref="object"/>.
            /// </returns>
            public object ResolveOptional(Type serviceType, ILifetimeScope scope = null)
            {
                return (scope ?? this.Scope()).ResolveOptional(serviceType);
            }

            /// <summary>
            /// The inject properties.
            /// </summary>
            /// <param name="instance">
            /// The instance.
            /// </param>
            /// <param name="scope">
            /// The scope.
            /// </param>
            /// <typeparam name="T">
            /// </typeparam>
            /// <returns>
            /// The <see cref="T"/>.
            /// </returns>
            public T InjectProperties<T>(T instance, ILifetimeScope scope = null)
            {
                return (scope ?? this.Scope()).InjectProperties(instance);
            }

            /// <summary>
            /// The inject unset properties.
            /// </summary>
            /// <param name="instance">
            /// The instance.
            /// </param>
            /// <param name="scope">
            /// The scope.
            /// </param>
            /// <typeparam name="T">
            /// </typeparam>
            /// <returns>
            /// The <see cref="T"/>.
            /// </returns>
            public T InjectUnsetProperties<T>(T instance, ILifetimeScope scope = null)
            {
                return (scope ?? this.Scope()).InjectUnsetProperties(instance);
            }

            /// <summary>
            ///     The scope.
            /// </summary>
            /// <returns>
            ///     The <see cref="ILifetimeScope" />.
            /// </returns>
            public ILifetimeScope Scope()
            {
                ILifetimeScope scope = null;

                /*try
                {
                    scope = AutofacDependencyResolver.Current.RequestLifetimeScope;
                }
                catch { }
    #1#
                if (scope == null)
                {
                    // really hackisch. But strange things are going on ?? :-)
                    scope = this.Container.BeginLifetimeScope("AutofacWebRequest");
                }

                return scope ?? this.Container;
            }*/
}

    /*

    public static class ContainerManagerExtensions
    {
		public static IRegistrationBuilder<TLimit, TReflectionActivatorData, TStyle> WithStaticCache<TLimit, TReflectionActivatorData, TStyle>(this IRegistrationBuilder<TLimit, TReflectionActivatorData, TStyle> registration) where TReflectionActivatorData : ReflectionActivatorData
		{
			return registration.WithParameter(Autofac.Core.ResolvedParameter.ForNamed<ICacheManager>("static"));
		}

		public static IRegistrationBuilder<TLimit, TReflectionActivatorData, TStyle> WithAspNetCache<TLimit, TReflectionActivatorData, TStyle>(this IRegistrationBuilder<TLimit, TReflectionActivatorData, TStyle> registration) where TReflectionActivatorData : ReflectionActivatorData
		{
			return registration.WithParameter(Autofac.Core.ResolvedParameter.ForNamed<ICacheManager>("aspnet"));
		}

		public static IRegistrationBuilder<TLimit, TReflectionActivatorData, TStyle> WithNullCache<TLimit, TReflectionActivatorData, TStyle>(this IRegistrationBuilder<TLimit, TReflectionActivatorData, TStyle> registration) where TReflectionActivatorData : ReflectionActivatorData
		{
			return registration.WithParameter(Autofac.Core.ResolvedParameter.ForNamed<ICacheManager>("null"));
		}

    }
*/
}